<?php

/*
 * Camelot PHP Tools
 *
 * @version 2.0
 * @author Bendsoft
 * @package PHP Tools
 * @subpackage Camelot
 * @license FreeBSD License (www.bendsoft.com/licensing/)
 * 
 */

/**
 * Handle SharePoint document libraries
 * 
 * @author Bendsoft
 */
class SharePointDocumentLibrary extends CamelotExceptionHandling {

    /**
     * Reference to the settingsarray with connections
     * @var string
     */
    public $connection_name;

    /**
     * Which Document Library to fetch
     * @var string
     */
    public $documentLibrary;

    /**
     * Array containing all folder paths and their ID (as key) from the document library
     * @var array
     */
    public $folders = array();

    /**
     * Array containing the folders ID as key and the folders parent ID as value
     * @var array
     */
    public $foldersParentID = array();

    /**
     * Property holding an ArrayObject with CamelotDocumentLibraryObjects
     * @var CamelotDocumentLibraryObject
     */
    public $CamelotDocumentLibraryObject;

    /**
     * Construct the class instance
     * @param string $connection_name Which connection to use from the settings
     * @param string $documentLibrary Which document library to fetch from sharepoint
     */
    public function __construct($connection_name, $documentLibrary) {
        $this->connection_name = $connection_name;
        $this->documentLibrary = $documentLibrary;
        $this->CamelotDocumentLibraryObject = new ArrayObject();

        $this->fill();
    }

    /**
     * Fill the CamelotDocumentLibraryObject with data
     * @todo Replace the try catch with some better solution
     * @return void
     */
    private function fill() {

        // In some cases the Title is not filled, this casuses the query to crash, thus the ugly solution with try catch
        try {
            $columns = "ID,GUID,DocIcon,`File Type`,Title,BaseName,LinkFilename,ContentType,ContentTypeId,FileSizeDisplay,_UIVersionString,ServerUrl,EncodedAbsUrl,Created,Modified,Author,Editor,`Modified By`,`Created By`,_ModerationStatus,WorkflowVersion,WorkflowInstanceID ";

            $command = new SharePointSqlCommand();
            $command->connection_name = $this->connection_name;
            $command->sql = "SELECT $columns FROM `$this->documentLibrary` ORDER BY Order LIMIT 10000";

            $GetLibrary = new SharePointQuery($command);
            $useTitle = true;
        } catch (Exception $exc) {
            $columns = "ID,GUID,DocIcon,`File Type`,BaseName,LinkFilename,ContentType,ContentTypeId,FileSizeDisplay,_UIVersionString,ServerUrl,EncodedAbsUrl,Created,Modified,Author,Editor,`Modified By`,`Created By`,_ModerationStatus,WorkflowVersion,WorkflowInstanceID ";

            $command = new SharePointSqlCommand();
            $command->connection_name = $this->connection_name;
            $command->sql = "SELECT $columns FROM `$this->documentLibrary` ORDER BY Order LIMIT 10000";

            $GetLibrary = new SharePointQuery($command);
            $useTitle = false;
        }

        if ($GetLibrary->exception instanceof CamelotException) {
            $this->commandStatus = false;
            $this->exception = $GetLibrary->exception;
            return $GetLibrary;
        }

        $this->commandStatus = true;

        if (is_null($GetLibrary->CamelotSoap->_sorted->_content))
            return;

        foreach ($GetLibrary->CamelotSoap->_sorted->_content as $document) {

            $document = (object) $document;

            // Fetched from SharePoint
            $ID = $document->ID;
            $GUID = $document->GUID;
            $DocIcon = $this->nullCheck($document->DocIcon);
            $FileType = $this->nullCheck($document->File_x0020_Type);
            $Title = ($useTitle) ? $this->nullCheck($document->Title) : $document->BaseName;
            $BaseName = $document->BaseName;
            $LinkFilename = $document->LinkFilename;
            $ContentType = $document->ContentType;
            $ContentTypeId = $document->ContentTypeId;
            $FileSizeDisplay = $this->nullCheck($document->FileSizeDisplay);
            $_UIVersionString = $document->_UIVersionString;
            $ServerUrl = $document->ServerUrl;
            $EncodedAbsUrl = $document->EncodedAbsUrl;
            $Created = $document->Created;
            $Modified = $document->Modified;
            $Author = $document->Author;
            $Editor = $document->Editor;
            $ModifiedBy = $this->nullCheck($document->Modified_x0020_By);
            $CreatedBy = $this->nullCheck($document->Created_x0020_By);
            $_ModerationStatus = $document->_ModerationStatus;
            $WorkflowVersion = $this->nullCheck($document->WorkflowVersion);
            $WorkflowInstanceID = $this->nullCheck($document->WorkflowInstanceID);

            $ParentID = null;
            $Level = null;
            $ParentName = null;
            $ParentPath = null;
            $FolderPath = null;
            $FileSize = null;

            // Add the fetched values, we null the custom properties for now and add them later
            $camelotDocumentLibraryObject = new CamelotDocumentLibraryObject($ID, $GUID, $DocIcon, $FileType, $Title, $BaseName, $LinkFilename, $ContentType, $ContentTypeId, $FileSizeDisplay, $_UIVersionString, $ServerUrl, $EncodedAbsUrl, $Created, $Modified, $Author, $Editor, $ModifiedBy, $CreatedBy, $_ModerationStatus, $WorkflowVersion, $WorkflowInstanceID, $ParentID, $Level, $ParentName, $ParentPath, $FolderPath, $FileSize);
            $this->CamelotDocumentLibraryObject->append($camelotDocumentLibraryObject);
        }


        // Array the object, for speeeeeed!
        $documents = (array) $this->CamelotDocumentLibraryObject;

        // Fill the custom properties
        foreach ($documents as $document) {
            $document->ParentPath = $this->getParentPath($document->ServerUrl); // 0.064040184020996 
            $document->ParentName = $this->getParentName($document->ServerUrl); // 0.061984062194824
            $document->Level = $this->getLevel($document->ServerUrl); // 0.2454628944397 | 0.047329187393188
            $document->ParentID = $this->getParentId($this->folders, $document->ParentPath); // 12.301592826843
            $document->FileSize = $this->getFileSize($document->FileSizeDisplay); //0.038778066635132
            $document->FolderPath = $this->getFolderPath($document->ServerUrl, $document->ContentType); // 0.069738149642944
            // Store the ServerUrl and ID in a separate array for faster search
            if ($document->ContentType == "Folder") {
                $this->folders[$document->ID] = $document->ServerUrl;
                $this->foldersParentID[$document->ID] = $document->ParentID;
            }
        }
    }

    /**
     * @param mixed $value
     * @return bool
     */
    private function nullCheck($value) {
        return ($value != "") ? $value : "";
    }

    /**
     * Get the path to the actual folder without the basefolder included
     * "/Shared Documents/Folder1/Folder2" should return "Folder1/Folder2/"
     * This method is only used to resolve folder paths
     * @param string $serverUrl
     * @return string 
     */
    private function getFolderPath($serverUrl, $contentType) {
        if ($contentType == "Folder") {
            $serverUrlArray = explode("/", $serverUrl);
            return implode("/", array_slice(explode("/", $serverUrl), 0, count($serverUrlArray))) . "/";
        } else {
            $serverUrlArray = explode("/", $serverUrl);
            return implode("/", array_slice($serverUrlArray, 0, count($serverUrlArray) - 1)) . "/";
        } // implode("/", array_slice(explode("/", $serverUrl), 0, -1));
    }

    /**
     * Convert the filezise to somthing readable
     * @param int $size
     * @return string
     */
    private function getFileSize($size) {
        $conversionValue = 1000;

        $size = (int) $size;

        if ($size < $conversionValue) // code to format fsize as bytes
            return "$size B";
        if (($size > 1024) && ($size < ($conversionValue * $conversionValue))) // code to format fsize as KB
            return round($size / $conversionValue, 2) . " KB";
        if ($size > ($conversionValue * $conversionValue)) // code to format fsize as MB
            return round($size / ($conversionValue * $conversionValue), 2) . " MB";

        return "";
    }

    /**
     * Get the level of an item in a folder
     * 1 = items at root level
     * 2 = item in a folder
     * 3 = ...
     * @param string $serverUrl
     * @return string 
     */
    private function getLevel($serverUrl) {
        return count(explode("/", $serverUrl));
    }

    /**
     * Return the name of the parent folder
     * "﻿/Shared Documents/Folder1/mod_helloworld_1_7.zip" should return "Folder1"
     * @param string $serverUrl
     * @return string 
     */
    private function getParentName($serverUrl) {
        $serverUrlArray = explode("/", $serverUrl);

        if (count($serverUrlArray) < 2)
            return $this->documentLibrary;

        $slice = array_slice($serverUrlArray, count($serverUrlArray) - 2, -1);
        return $slice[0];
    }

    /**
     * Retrieve the path of the parent folder
     * ﻿"/Shared Documents/Folder1/mod_helloworld_1_7.zip" should return
     * "/Shared Documents/Folder1"
     * @param string $serverUrl
     * @return string 
     */
    private function getParentPath($serverUrl) {
        return implode("/", array_slice(explode("/", $serverUrl), 0, -1));
    }

    /**
     * Find parent id based on path
     * @param array $folders
     * @param string $parentPath
     * @return int 
     */
    private function getParentId($folders, $parentPath) {
        $parentId = 0;
        foreach ($folders as $key => $folder) {
            if ($folder == $parentPath) {
                $parentId = $key;
                break;
            }
        }
        return $parentId;
    }

}

/**
 * Default document library object
 * 
 * @author Bendsoft
 */
class CamelotDocumentLibraryObject {

    // Custom properties
    /**
     * 
     * @var int
     */
    public $ParentID;

    /**
     * 
     * @var int
     */
    public $Level;

    /**
     * 
     * @var string
     */
    public $ParentName;

    /**
     * 
     * @var string
     */
    public $ParentPath;

    /**
     * 
     * @var string
     */
    public $FolderPath;

    /**
     * 
     * @var string
     */
    public $FileSize;
    // Selected SharePoint properties
    /**
     * Nullable int
     * @var int
     */
    public $ID;

    /**
     * Nullable GUID
     * @var Guid
     */
    public $GUID;

    /**
     * Associated document icon, usually same as FileType
     * @var string
     */
    public $DocIcon;

    /**
     * File Type (ex jpg, data, txt)
     * @var string
     */
    public $FileType;

    /**
     * Updatable // Not always set
     * @var string
     */
    public $Title;

    /**
     * Only name, no extention
     * @var string
     */
    public $BaseName;

    /**
     * Entire name + extension
     * @var string
     */
    public $LinkFilename;

    /**
     * Folder, Document // Updatable
     * @var string
     */
    public $ContentType;

    /**
     * Hex code of Content types
     * @var string
     */
    public $ContentTypeId;

    /**
     * File size in bytes
     * Nullable int
     * @var int
     */
    public $FileSizeDisplay;

    /**
     * Version info
     * @var string
     */
    public $_UIVersionString;

    /**
     * 
     * @var string
     */
    public $ServerUrl;

    /**
     * 
     * @var string
     */
    public $EncodedAbsUrl;

    /**
     * DateTime formatted string
     * @var DateTime
     */
    public $Created;

    /**
     * DateTime formatted string
     * @var DateTime
     */
    public $Modified;

    /**
     * Nullable int
     * @var int
     */
    public $Author;

    /**
     * Nullable int
     * @var int
     */
    public $Editor;

    /**
     * Modified By
     * @var string
     */
    public $ModifiedBy;

    /**
     * Created By
     * @var string
     */
    public $CreatedBy;

    /**
     * Nullable int
     * @var int
     */
    public $_ModerationStatus;

    /**
     * Nullable int
     * @var int
     */
    public $WorkflowVersion;

    /**
     * Nullable GUID
     * @var Guid
     */
    public $WorkflowInstanceID;

    /**
     *
     * @param int $ParentID
     * @param int $Level
     * @param string $ParentName
     * @param string $ParentPath
     * @param string $FolderPath
     * @param string $FileSize
     * @param int $ID
     * @param Guid $GUID
     * @param string $DocIcon
     * @param string $FileType
     * @param string $Title
     * @param string $BaseName
     * @param string $LinkFilename
     * @param string $ContentType
     * @param string $ContentTypeId
     * @param int $FileSizeDisplay
     * @param string $_UIVersionString
     * @param string $ServerUrl
     * @param string $EncodedAbsUrl
     * @param DateTime $Created
     * @param DateTime $Modified
     * @param int $Author
     * @param int $Editor
     * @param string $ModifiedBy
     * @param string $CreatedBy
     * @param int $_ModerationStatus
     * @param int $WorkflowVersion
     * @param Guid $WorkflowInstanceID 
     */
    public function __construct($ID = null, $GUID = null, $DocIcon, $FileType, $Title, $BaseName, $LinkFilename, $ContentType, $ContentTypeId, $FileSizeDisplay = null, $_UIVersionString, $ServerUrl, $EncodedAbsUrl, $Created, $Modified, $Author = null, $Editor = null, $ModifiedBy, $CreatedBy, $_ModerationStatus = null, $WorkflowVersion = null, $WorkflowInstanceID = null, $ParentID, $Level, $ParentName, $ParentPath, $FolderPath, $FileSize) {
        $this->ParentID = $ParentID;
        $this->Level = $Level;
        $this->ParentName = $ParentName;
        $this->ParentPath = $ParentPath;
        $this->FolderPath = $FolderPath;
        $this->FileSize = $FileSize;
        $this->ID = $ID;
        $this->GUID = $GUID;
        $this->DocIcon = $DocIcon;
        $this->FileType = $FileType;
        $this->Title = $Title;
        $this->BaseName = $BaseName;
        $this->LinkFilename = $LinkFilename;
        $this->ContentType = ($this->startsWith($ContentTypeId, SharePointContentTypes::GetContentType("Folder"))) ? "Folder" : $ContentType;
        $this->ContentTypeId = $ContentTypeId;
        $this->FileSizeDisplay = $FileSizeDisplay;
        $this->_UIVersionString = $_UIVersionString;
        $this->ServerUrl = $ServerUrl;
        $this->EncodedAbsUrl = $EncodedAbsUrl;
        $this->Created = $Created;
        $this->Modified = $Modified;
        $this->Author = $Author;
        $this->Editor = $Editor;
        $this->ModifiedBy = $ModifiedBy;
        $this->CreatedBy = $CreatedBy;
        $this->_ModerationStatus = $_ModerationStatus;
        $this->WorkflowVersion = $WorkflowVersion;
        $this->WorkflowInstanceID = $WorkflowInstanceID;
    }

    /**
     * 
     * @param string $haystack
     * @param string $needle
     * @return bool
     */
    private function startsWith($haystack, $needle) {
        return (substr($haystack, 0, strlen($needle))==$needle);
    }

}

?>
